package org.hackystat.ui.chart.jfc.server;

import java.io.File;
import java.util.Enumeration;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import org.hackystat.ui.chart.jfc.chartresource.ChartManager;
import org.hackystat.ui.chart.jfc.chartresource.ChartResource;
import org.hackystat.ui.chart.jfc.fileresource.ChartFileManager;
import org.hackystat.ui.chart.jfc.fileresource.ChartFileResource;
import org.hackystat.utilities.logger.HackystatLogger;
import org.restlet.Application;
import org.restlet.Component;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.data.Protocol;

/**
 * The JFreeChart Service HTTP server.
 *
 * @author Pavel Senin.
 */
public class Server
    extends Application {

  static final Integer defaultPort = 3128;

  /**
   * This section is belong to the Apache Commons-CLI library and defines variables needed for the
   * command line interface.
   */

  /** The software general description. */
  static final String descMessage = "The Hackystat-UI-Chart JFreeChart Service component.\n"
      + "This service handles REST-based requests, renders charts using JFreeChart,\n"
      + "hosts those and sends chart URL back to the client.";

  /** The help message. */
  static final String helpMessage = "prints this message";

  /** The port message. */
  static final String portMessage = "specifies the HTTP port number";

  /** The server port number. */
  static Integer serverPortNum;

  /** The server temporarily storage folder. */
  static String serverStorageFolder;

  /** The storage message. */
  static final String storageMessage = "specifies the storage folder";

  /** The command-line syntax. */
  static final String syntaxMessage = "java -jar hackystat-ui-chart-jfc.jar "
      + "<option> <value> ...";

  /**
   * Holds the Restlet Component associated with this Server.
   */
  private Component serverComponent;

  /**
   * Holds the host name associated with this Server.
   */
  private String hostName;

  /** Holds the HackystatLogger for the sensorbase. */
  private Logger logger;

  /**
   * Creates a new instance of a JFreeChart Service HTTP server, listening on the supplied port.
   *
   * @param portN the port number for the query server.
   * @param tmpD the folder to use as the temporarily storage for the charts.
   * @throws Exception If problems occur starting up this server.
   * @return the new JFCS server instance.
   */
  public static Server newInstance(Integer portN, String tmpD) throws Exception {

    Server server = new Server();

    server.logger = HackystatLogger.getLogger("org.hackystat.ui.chart");

    serverPortNum = portN;
    serverStorageFolder = tmpD;

    /** specifying the server general settings. */

    server.serverComponent = new Component();

    server.hostName = "http://localhost:" + portN;

    server.serverComponent.getServers().add(Protocol.HTTP, portN);

    server.serverComponent.getDefaultHost().attach("/jfcs", server);

    /** and finally starting the server. */
    server.serverComponent.start();

    // Disabling restlet logger.
    disableRestletLogging();
    // Attaching own logger.
    server.logger.warning("Started hackystat-ui-chart (Version " + getVersion() + ")");
    server.logger.warning("Host: " + server.hostName);
    server.logger.warning("Temporarily storage placed in: " + tmpD);
    HackystatLogger.setLoggingLevel(server.logger, "INFO");

    // Save a pointer to this Server instance in this Application's context.
    Map<String, Object> attributes = server.getContext().getAttributes();

    // Note: there is no order specified for instantiating the components.
    attributes.put("ChartManager", new ChartManager(server));
    attributes.put("ChartFileManager", new ChartFileManager(server));

    // that's it.
    return server;
  }

  /**
   * Starts up the SensorBase web service on port 9876. Control-c to exit.
   *
   * @param args Ignored.
   * @throws Exception if problems occur.
   */
  public static void main(final String[] args) throws Exception {

    Integer specifiedPortNum = Server.defaultPort;
    String specifiedStorageDir = System.getProperty("java.io.tmpdir");
    /** parsing command-line parameters first. */
    if (args.length == 0) {
      Server.printHelpMessage("No parameters specified:");
    }
    else if (args.length == 1) {
      String tmpArg = args[0];
      if (tmpArg.contains("help")) {
        Server.printHelpMessage("Help:");
      }
    }
    else if (args.length == 4) {

      /** extracting args and values */
      String argName1 = args[0];
      String argValue1 = args[1];

      String argName2 = args[2];
      String argValue2 = args[3];

      /** parsing port argument */
      if ("-port".equalsIgnoreCase(argName1)) {
        specifiedPortNum = parsePort(argValue1);
      }
      else if ("-port".equalsIgnoreCase(argName2)) {
        specifiedPortNum = parsePort(argValue2);
      }
      else {
        Server.printHelpMessage("no -port argument specified");
      }

      if ("-storage".equalsIgnoreCase(argName1)) {
        specifiedStorageDir = parseStorage(argValue1);
      }
      else if ("-storage".equalsIgnoreCase(argName2)) {
        specifiedStorageDir = parseStorage(argValue2);
      }
      else {
        Server.printHelpMessage("no -storage argument specified");
      }

    }

    // finally if everything worked out create the server instance.
    Server.newInstance(specifiedPortNum, specifiedStorageDir);
  }

  /**
   * Parses port command line value.
   *
   * @param argValue1 the value to parse.
   * @return the port number.
   */
  private static Integer parsePort(String argValue1) {
    Integer result = null;
    try {
      result = Integer.valueOf(argValue1);
    }
    catch (NumberFormatException ne) {
      System.err.println("Cannot parse -port value specified \"" + argValue1 + "\", default "
          + Server.defaultPort + " value used instead");
    }
    if (null == result) {
      return Server.defaultPort;
    }
    else {
      return result;
    }
  }

  /**
   * Parses the -storage argument provided.
   *
   * @param argValue1 the argument value given.
   * @return the folder to use as the temporarili storage.
   */
  private static String parseStorage(String argValue1) {

    String systemStorageName = System.getProperty("java.io.tmpdir");

    if (new File(argValue1).exists()) {
      return argValue1;
    }
    else {
      return systemStorageName;
    }
  }

  /**
   * Prints help message to console.
   *
   * @param msg the optional message to print.
   *
   */
  private static void printHelpMessage(String msg) {
    if (null == msg) {
      assert true;
    }
    else {
      System.out.format("Error: %s\n\n", msg);
    }
    System.out.format("%s\n\n", Server.descMessage);
    System.out.format("Usage:\n %s\n\nwhere options are:\n", Server.syntaxMessage);
    System.out.format(" -help      %s\n", Server.helpMessage);
    System.out.format(" -port      %s\n", Server.portMessage);
    System.out.format(" -storage   %s\n", Server.storageMessage);
    System.exit(10);
  }

  /**
   * Dispatch to the rendered charts or to the request processing.
   *
   * @return The router Restlet.
   */
  @Override
  public Restlet createRoot() {

    Router xRouter = new Router(getContext());

    // attaching the RequestServer to the /charttypes URI.
    xRouter.attach("/charttypes", ChartResource.class);
    xRouter.attach("/chart/{telemetrytype}", ChartResource.class);
    xRouter.attach("/chart/{telemetrytype}?startTime={startTime}&endTime={endTime}",
        ChartResource.class);

    // attaching the RequestServer to the /drawchart URI.
    xRouter.attach("/chart", ChartResource.class);
    xRouter.attach("/chart/{telemetrytype}", ChartResource.class);
    xRouter.attach("/chart/{telemetrytype}?startTime={startTime}&endTime={endTime}",
        ChartResource.class);
    xRouter.attach("/chart/{telemetrytype}?startTime={startTime}&endTime={endTime}"
        + "&afferentCouplings={afferent}&efferentCouplings={efferent}&cycles={cycles}",
        ChartResource.class);

    // attaching the ChartServer to the /getchart URI.
    xRouter.attach("/file", ChartFileResource.class);
    xRouter.attach("/file/{chartname}", ChartFileResource.class);

    return xRouter;
  }

  /**
   * Reports the folder which is used for the temporarily storage.
   *
   * @return temporarily storage.
   */
  public String getStoragePath() {
    return serverStorageFolder;
  }

  /**
   * Returns the version associated with this Package, if available from the jar file manifest. If
   * not being run from a jar file, then returns "Development".
   *
   * @return The version.
   */
  public static String getVersion() {
    String version = Package.getPackage("org.hackystat.ui.chart.jfc.server")
        .getImplementationVersion();
    return (version == null) ? "Development" : version;
  }

  /**
   * Disable all loggers from com.noelios and org.restlet.
   */
  private static void disableRestletLogging() {
    LogManager logManager = LogManager.getLogManager();
    for (Enumeration e = logManager.getLoggerNames(); e.hasMoreElements();) {
      String logName = e.nextElement().toString();
      if (logName.startsWith("com.noelios") || logName.startsWith("org.restlet")) {
        logManager.getLogger(logName).setLevel(Level.OFF);
      }
    }
  }

  /**
   * Returns the host name associated with this server.
   * Example: "http://localhost:3128/chartserver/"
   * @return The host name.
   */
  public String getHostName() {
    return this.hostName;
  }
}
